package cn.z2huo.knowledge.concurrency.threadpool.schedule;

import cn.hutool.core.thread.ThreadUtil;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>
 *
 * @author z2huo
 */
@Slf4j
class ScheduleThreadPoolTest {

    private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);

    public void scheduleRunnable() {
        log.info("now is {}", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));

        ScheduledFuture<?> future = executor.schedule(() -> {
            log.info("schedule task, delay 10s, time is {}", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));
        }, 10, TimeUnit.SECONDS);

        long delay = future.getDelay(TimeUnit.SECONDS);
        log.info("future getDelay() return is {}", delay);
        ThreadUtil.sleep(1, TimeUnit.SECONDS);
        log.info("future getDelay() return is {}", future.getDelay(TimeUnit.SECONDS));
        ThreadUtil.sleep(1, TimeUnit.SECONDS);
        log.info("future getDelay() return is {}", future.getDelay(TimeUnit.SECONDS));
        ThreadUtil.sleep(1, TimeUnit.SECONDS);
        log.info("future getDelay() return is {}", future.getDelay(TimeUnit.SECONDS));

        try {
            Object value = future.get();
            log.info("future return value is {}", value);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    public void scheduleCallable() {
        log.info("now is {}", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));

        ScheduledFuture<String> future = executor.schedule(() -> {
            log.info("schedule task, delay 10s, time is {}", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));
            return "Callable return";
        }, 10, TimeUnit.SECONDS);

        log.info("future getDelay() return is {}", future.getDelay(TimeUnit.SECONDS));
        ThreadUtil.sleep(1, TimeUnit.SECONDS);
        log.info("future getDelay() return is {}", future.getDelay(TimeUnit.SECONDS));
        ThreadUtil.sleep(1, TimeUnit.SECONDS);
        log.info("future getDelay() return is {}", future.getDelay(TimeUnit.SECONDS));
        ThreadUtil.sleep(1, TimeUnit.SECONDS);
        log.info("future getDelay() return is {}", future.getDelay(TimeUnit.SECONDS));
        ThreadUtil.sleep(1, TimeUnit.SECONDS);

        try {
            String value = future.get();
            log.info("future return value is {}", value);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    public void scheduleAtFixedRate() {
        log.info("now is {}", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));

        ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> {
            log.info("schedule task at fixed rate, time is {}", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));
            log.info("process task, task execution time 3 seconds");
            ThreadUtil.sleep(3, TimeUnit.SECONDS);
        }, 3, 5, TimeUnit.SECONDS);

        log.info("future getDelay() return is {}", future.getDelay(TimeUnit.SECONDS));
        ThreadUtil.sleep(1, TimeUnit.SECONDS);
        log.info("future getDelay() return is {}", future.getDelay(TimeUnit.SECONDS));
        ThreadUtil.sleep(3, TimeUnit.SECONDS);
        log.info("future getDelay() return is {}", future.getDelay(TimeUnit.SECONDS));
        ThreadUtil.sleep(5, TimeUnit.SECONDS);
    }

    public void scheduleWithFixedDelay() {
        log.info("now is {}", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));

        ScheduledFuture<?> future = executor.scheduleWithFixedDelay(() -> {
            log.info("schedule task at fixed rate, time is {}", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));
            log.info("process task, task execution time 3 seconds");
            ThreadUtil.sleep(3, TimeUnit.SECONDS);
        }, 3, 5, TimeUnit.SECONDS);

        log.info("future getDelay() return is {}", future.getDelay(TimeUnit.SECONDS));
        ThreadUtil.sleep(1, TimeUnit.SECONDS);
        log.info("future getDelay() return is {}", future.getDelay(TimeUnit.SECONDS));
        ThreadUtil.sleep(3, TimeUnit.SECONDS);
        log.info("future getDelay() return is {}", future.getDelay(TimeUnit.SECONDS));
        ThreadUtil.sleep(5, TimeUnit.SECONDS);
    }

    /**
     * 任务中出现异常
     */
    public void scheduleAtFixedRateException() {

        log.info("now is {}", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));

        AtomicInteger i = new AtomicInteger(1);

        ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> {
            log.info("schedule task at fixed rate, time is {}", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));
            if (i.getAndIncrement() % 4 == 0) {
                int j = 1 / 0;
            }
        }, 3, 5, TimeUnit.SECONDS);

        // 没有下面的 future.get() 则任务因为异常终止不会继续执行后续的周期性任务，但是异常也不会返回
        try {
            Object value = future.get();
            log.info("future return value is {}", value);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void scheduleAtFixedRateExceptionCatch() {

        log.info("now is {}", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));

        AtomicInteger i = new AtomicInteger(1);

        ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> {
            try {
                log.info("schedule task at fixed rate, time is {}", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));
                if (i.getAndIncrement() % 4 == 0) {
                    int j = 1 / 0;
                }
            } catch (Exception e) {
                log.error("task error", e);
            }
        }, 3, 5, TimeUnit.SECONDS);

        // 没有下面的 future.get() 则任务因为异常终止不会继续执行后续的周期性任务，但是异常也不会返回
        try {
            Object value = future.get();
            log.info("future return value is {}", value);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void scheduleWithFixedDelayCancel() {
        log.info("now is {}", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));

        ScheduledFuture<?> future = executor.scheduleWithFixedDelay(() -> {
            log.info("schedule task at fixed rate, time is {}", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));
        }, 3, 5, TimeUnit.SECONDS);

        log.info("future getDelay() return is {}", future.getDelay(TimeUnit.SECONDS));
        ThreadUtil.sleep(1, TimeUnit.SECONDS);

        future.cancel(true);
    }

    public static void main(String[] args) {

        ScheduleThreadPoolTest test = new ScheduleThreadPoolTest();

        try {
            //test.scheduleRunnable();
            //test.scheduleCallable();
            //test.scheduleAtFixedRate();
            //test.scheduleWithFixedDelay();
            //test.scheduleAtFixedRateException();
            //test.scheduleAtFixedRateExceptionCatch();
            test.scheduleWithFixedDelayCancel();

            ThreadUtil.sleep(70, TimeUnit.SECONDS);
        } finally {
            // 这里不将 executor 关闭的话，scheduleWithFixedDelay 和 scheduleAtFixedRate 会一直持续下去
            test.executor.close();
        }
    }

}
